package com.sen.common.orm.ibatis;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Arrays;

import org.apache.log4j.Logger;

import com.ibatis.sqlmap.engine.execution.SqlExecutor;
import com.ibatis.sqlmap.engine.mapping.statement.RowHandlerCallback;
import com.ibatis.sqlmap.engine.scope.RequestScope;
import com.sen.common.orm.Dialect;
import com.sen.common.orm.DialectFactory;
import com.sen.common.orm.PageContext;

/**
 * ibatis里的分页SQL语句执行代理.
 * 
 * @author sen
 */
public class LimitSqlExecutor extends SqlExecutor {
	private static final Logger LOG = Logger.getLogger(LimitSqlExecutor.class);

	private boolean enableLimit = true;
	private boolean enableSqlLmitRecordCount = true;

	private Dialect dialet;

	private String dbType;

	public boolean isEnableLimit() {
		return enableLimit;
	}

	public void setEnableLimit(boolean enableLimit) {
		this.enableLimit = enableLimit;
	}

	public void executeQuery(RequestScope request, Connection conn, String sql, Object[] parameters, int skipResults,
			int maxResults, RowHandlerCallback callback) throws SQLException {
		if (dialet == null) {
			dialet = DialectFactory.getDialect(dbType);
		}
		try {
			if ((skipResults != NO_SKIPPED_RESULTS || maxResults != NO_MAXIMUM_RESULTS) && isEnableLimit()) {
				if (LOG.isDebugEnabled()) {
					LOG.debug("原始sql: " + sql);
					LOG.debug("查询参数parameters: " + Arrays.toString(parameters));
				}
				
				if (isEnableSqlLmitRecordCount() && PageContext.isCountTotalPermitted()) {
					int limitSqlRecordCount = getSqlCount(conn, sql, parameters);
					PageContext.setTotalElements(Integer.valueOf(limitSqlRecordCount));
				}
				
				sql = dialet.getPaginationSql(sql, skipResults, maxResults);
				if (LOG.isDebugEnabled()) {
				    LOG.debug("转换后的sql: " + sql);
				}

				// 不进行ibatis的逻辑分页
				skipResults = NO_SKIPPED_RESULTS;
				maxResults = NO_MAXIMUM_RESULTS;
			}

			super.executeQuery(request, conn, sql, parameters, skipResults, maxResults, callback);
		} catch (SQLException ex) {
			LOG.error("数据库执行报错，SQL语句为: " + sql, ex);
			throw ex;
		}
	}

	public int executeUpdate(RequestScope request, Connection conn, String sql, Object[] parameters)
			throws SQLException {
		try {
			return super.executeUpdate(request, conn, sql, parameters);
		} catch (SQLException ex) {
			LOG.error("数据库执行报错，SQL语句为:" + sql, ex);
			throw ex;
		}
	}

	private int getSqlCount(Connection conn, String sql, Object[] parameters) {
		ResultSet rt = null;
		PreparedStatement st = null;
		try {
			String statement = dialet.getCountSql(sql);
			st = conn.prepareStatement(statement);
			int count = countString(sql, "?");
			for (int i = 0; i != count; ++i) {
				if (parameters[i] == null) {
					st.setNull(i + 1, 0);
					continue;
				}
				if (parameters[i] instanceof java.sql.Date) {
					st.setDate(i + 1, (java.sql.Date) parameters[i]);
				} else if (parameters[i] instanceof Timestamp) {
					st.setTimestamp(i + 1, (Timestamp) parameters[i]);
				} else if (parameters[i] instanceof java.util.Date) {
					st.setTimestamp(i + 1, new Timestamp(((java.util.Date) parameters[i]).getTime()));
				} else {
					st.setObject(i + 1, parameters[i]);
				}
			}
			rt = st.executeQuery();
			if (rt.next()) {
				return rt.getInt(1);
			}
		} catch (Exception e) {
			LOG.error("分页获取记录总数失败！\n执行sql为：" + sql, e);
		} finally {
			try {
				rt.close();
			} catch (SQLException e) {
				LOG.error("关闭数据库资源出错：" + sql, e);
			}
			try {
				st.close();
			} catch (SQLException e) {
				LOG.error("关闭数据库资源出错：" + sql, e);
			}
		}
		return 0;
	}

	private static int countString(String str, String s) {
		int temp = 0;
		int count = 0;
		while (true) {
            temp = str.indexOf(s, temp);
            if (temp == -1) {
                break;
            } else {
            	temp ++;
            }
            ++ count;
        }
		return count;
	}
	
	public boolean isEnableSqlLmitRecordCount() {
		return this.enableSqlLmitRecordCount;
	}

	public void setEnableSqlLmitRecordCount(boolean enableSqlLmitRecordCount) {
		this.enableSqlLmitRecordCount = enableSqlLmitRecordCount;
	}

	public void setDbType(String dbType) {
		this.dbType = dbType;
	}
	
	public String getDbType() {
		return dbType;
	}
}
